﻿using System;
using System.Net.Sockets;
using System.Diagnostics;
using System.Threading;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.IO.Pipelines;
using System.Buffers;
using Microsoft.Extensions.Logging;
using System.Net;
using Atomic;

namespace JTActiveSafety.Gateway.Internal
{
    public class SocketConnection
    {
        private static readonly int MinAllocBufferSize = 512;
        private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
        private static readonly bool IsMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);

        private readonly Socket _socket;
        private readonly ISocketsTrace _trace;
        private readonly CancellationTokenSource _connectionClosedTokenSource = new CancellationTokenSource();

        private readonly object _shutdownLock = new();
        private volatile bool _socketDisposed;
        private volatile Exception _shutdownReason;
        private Task _processingTask;
        private readonly TaskCompletionSource _waitForConnectionClosedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        private bool _connectionClosed;
        private readonly bool _waitForData;
        private string _connectionId;
        public IDuplexPipe Application { get; set; }

        public PipeWriter Input => Application.Output;
        public PipeReader Output => Application.Input;
        //public string ConnectionId { get; set; } = Guid.NewGuid().ToString();


        /// <summary>
        /// 最后活动(接收或发送数据成功为准，初始以建立连接为准)时间，相对于服务器启动/重启后的相对秒数
        /// </summary>
        //public long revTime { get; set; }
        public long LastActiveTime { get; private set; }
        public EndPoint RemoteEndPoint { get;private set; }
        public EndPoint LocalEndPoint { get;private set; }

        public virtual CancellationToken ConnectionClosed { get; set; }

        internal SocketConnection(Socket socket,
                                  MemoryPool<byte> memoryPool,
                                  PipeScheduler transportScheduler,
                                  ISocketsTrace trace,
                                  long? maxReadBufferSize = null,
                                  long? maxWriteBufferSize = null,
                                  bool waitForData = true,
                                  bool useInlineSchedulers = false)
        {
            Debug.Assert(socket != null);
            Debug.Assert(trace != null);

            _socket = socket;
            _trace = trace;

            LocalEndPoint = _socket.LocalEndPoint;
            RemoteEndPoint = _socket.RemoteEndPoint;

            ConnectionClosed = _connectionClosedTokenSource.Token;
        }

        public void Start()
        {
            _processingTask = StartAsync();
        }

        private async Task StartAsync()
        {
            await FillPipeAsync();
        }
        private async Task FillPipeAsync()
        {
            try
            {
                while (!_connectionClosedTokenSource.IsCancellationRequested)
                {
                    //if (token.IsCancellationRequested)
                    //{
                    //    return;
                    //}

                    try
                    {
                        Memory<byte> memory = Input.GetMemory(MinAllocBufferSize);

                        //设备多久没发数据就断开连接 Receive Timeout.
                        int bytesRead = await _socket.ReceiveAsync(memory, SocketFlags.None);
                        if (bytesRead == 0)
                        {
                            break;
                        }
                        Input.Advance(bytesRead);// 告诉PipeWriter从套接字读取了多少
                    }
                    //catch (SocketException ex)
                    //{

                    //    _trace.LogError($"{ex.Message}[{ex.SocketErrorCode}]:{_tcpSession.Client.RemoteEndPoint}");

                    //    break;
                    //}
                    //catch (ObjectDisposedException)
                    //{

                    //    _trace.LogError("[Receive Timeout]");

                    //    break;
                    //}
                    //catch (OperationCanceledException)
                    //{

                    //    _trace.LogError($"[Receive Timeout]:{_tcpSession.Client.RemoteEndPoint}");

                    //    break;
                    //}
                    catch (Exception)
                    {

                        _trace.LogError($"[Receive Error]:{RemoteEndPoint}");

                        break;
                    }

                    FlushResult result = await Input.FlushAsync();// 标记数据可用，让PipeReader读取
                    if (result.IsCompleted)
                    {
                        break;
                    }
                }
            }
            finally
            {
                await Input.CompleteAsync();// 告诉PipeReader没有更多的数据
            }
        }

        public virtual string ConnectionId
        {
            get
            {
                if (_connectionId == null)
                {
                    _connectionId = CorrelationIdGenerator.GetNextId();
                }

                return _connectionId;
            }
            set
            {
                _connectionId = value;
            }
        }

        public async void SendAsync(byte[] data, CancellationToken cancellationToken = default)
        {
            try
            {
                 await _socket.SendAsync(data, SocketFlags.None, cancellationToken);
            }
            catch (SocketException ex)
            {
                //cancellationToken.ThrowIfCancellationRequested();
                //Error Code https://docs.microsoft.com/zh-cn/windows/win32/winsock/windows-sockets-error-codes-2
                var x = ex.ErrorCode; //10054 对等方重置连接。
            }
            catch (Exception ex)
            {
                Abort(ex);
                //throw ex;
            }
        }
        public void Send(byte[] data)
        {
            try
            {
                _socket.Send(data, SocketFlags.None);
            }
            catch (SocketException ex)
            {
                //cancellationToken.ThrowIfCancellationRequested();
                //Error Code https://docs.microsoft.com/zh-cn/windows/win32/winsock/windows-sockets-error-codes-2
                var x = ex.ErrorCode; //10054 对等方重置连接。
            }
            catch (Exception ex)
            {
                Abort(ex);
                //throw ex;
            }
        }

        ///// <summary>
        ///// 重新连接，用于断线重试
        ///// </summary>
        //public virtual void Reconnect() { 

        //}

        public void Abort(Exception abortReason)
        {
            // Try to gracefully close the socket to match libuv behavior.
            Shutdown(abortReason);

            // Cancel ProcessSends loop after calling shutdown to ensure the correct _shutdownReason gets set.
            Output.CancelPendingRead();
            Input.CancelPendingFlush();
        }

        // Only called after connection middleware is complete which means the ConnectionClosed token has fired.
        public async ValueTask DisposeAsync()
        {
            Input.Complete();
            Output.Complete();

            if (_processingTask != null)
            {
                await _processingTask;
            }

            _connectionClosedTokenSource.Dispose();
        }
 

        //private void FireConnectionClosed()
        //{
        //    // Guard against scheduling this multiple times
        //    if (_connectionClosed)
        //    {
        //        return;
        //    }

        //    _connectionClosed = true;

        //    ThreadPool.UnsafeQueueUserWorkItem(state =>
        //    {
        //        state.CancelConnectionClosedToken();

        //        state._waitForConnectionClosedTcs.TrySetResult();
        //    },
        //    this,
        //    preferLocal: false);
        //}

        private void Shutdown(Exception shutdownReason)
        {
            lock (_shutdownLock)
            {
                if (_socketDisposed)
                {
                    return;
                }

                // Make sure to close the connection only after the _aborted flag is set.
                // Without this, the RequestsCanBeAbortedMidRead test will sometimes fail when
                // a BadHttpRequestException is thrown instead of a TaskCanceledException.
                _socketDisposed = true;

                // shutdownReason should only be null if the output was completed gracefully, so no one should ever
                // ever observe the nondescript ConnectionAbortedException except for connection middleware attempting
                // to half close the connection which is currently unsupported.
                _shutdownReason = shutdownReason ?? new Exception("The Socket transport's send loop completed gracefully.");

                _trace.ConnectionWriteFin(ConnectionId, _shutdownReason.Message);

                try
                {
                    // Try to gracefully close the socket even for aborts to match libuv behavior.
                    _socket.Shutdown(SocketShutdown.Both);
                }
                catch
                {
                    // Ignore any errors from Socket.Shutdown() since we're tearing down the connection anyway.
                }

                _socket.Dispose();
            }
        }

        //private void CancelConnectionClosedToken()
        //{
        //    try
        //    {
        //        _connectionClosedTokenSource.Cancel();
        //    }
        //    catch (Exception ex)
        //    {
        //        _trace.LogError(0, ex, $"Unexpected exception in {nameof(SocketConnection)}.{nameof(CancelConnectionClosedToken)}.");
        //    }
        //}

        private static bool IsConnectionResetError(SocketError errorCode)
        {
            // A connection reset can be reported as SocketError.ConnectionAborted on Windows.
            // ProtocolType can be removed once https://github.com/dotnet/corefx/issues/31927 is fixed.
            return errorCode == SocketError.ConnectionReset ||
                   errorCode == SocketError.Shutdown ||
                   (errorCode == SocketError.ConnectionAborted && IsWindows) ||
                   (errorCode == SocketError.ProtocolType && IsMacOS);
        }

        private static bool IsConnectionAbortError(SocketError errorCode)
        {
            // Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
            return errorCode == SocketError.OperationAborted ||
                   errorCode == SocketError.Interrupted ||
                   (errorCode == SocketError.InvalidArgument && !IsWindows);
        }
    }
}
